package parser

import scala.util.parsing.combinator._
import ewu._

/**
 * Die Klasse AdaChecker enthaelt Funktionen zum pruefen, ob ein uebergebener String
 * der MiniAda-Sprache dieses Programms entspricht.
 */

class AdaChecker extends JavaTokenParsers {
  
  def Programm: Parser[Any] = ImportBereich ~ HauptProzedur | HauptProzedur |
                              PackageBody
                              
  def PackageBody: Parser[Any] = packageP ~ bodyP ~ Identifier ~ isP ~ 
                                 ProzedurDeklarationen ~ endP ~ Identifier ~
                                 semikolonP
  def ProzedurDeklarationen: Parser[Any] = DeklarationsBereich
  def ImportBereich: Parser[Any] = Import ~ ImportBereich | Import  
  def Import: Parser[Any] = withP ~ LinkedName ~ semikolonP |
                            useP ~ LinkedName ~ semikolonP
  def HauptProzedur: Parser[Any] = Prozedur
  def Prozedur: Parser[Any] = procedureP ~ Identifier ~ ProzedurOptionals ~ beginP ~ 
                                  Anwfolge ~ endP ~ Identifier ~ semikolonP
  def ProzedurOptionals: Parser[Any] = openP ~ Parameter ~ closeP ~ isP ~ DeklarationsBereich |
                                       openP ~ Parameter ~ closeP ~ isP |
                                       isP ~ DeklarationsBereich
  def DeklarationsBereich: Parser[Any] = Deklaration ~ DeklarationsBereich |
                                         Deklaration
  def Deklaration: Parser[Any] = IdentifierList ~ dpunktP ~ TypeAngabe ~ 
                                      TypeInitialisierung ~ semikolonP |
                                 Prozedur
  def TypeAngabe: Parser[Any] = IntegerType | ArrayType
  def IntegerType: Parser[Any] = integerP
  def ArrayType: Parser[Any] = Identifier ~ ArrayDefBereich | Identifier
  def TypeInitialisierung: Parser[Any] = zuweisungP ~ AllWert | ""
  def Parameter: Parser[Any] = ParameterDef ~ semikolonP ~ Parameter |
                               ParameterDef
  def ParameterDef: Parser[Any] = IdentifierList ~ dpunktP ~ ParameterMode ~ 
                                      Identifier ~ zuweisungP ~ numberP |
                                  IdentifierList ~ dpunktP ~ Identifier ~ zuweisungP ~ 
                                      numberP |
                                  IdentifierList ~ dpunktP ~ ParameterMode ~ Identifier |
                                  IdentifierList ~ dpunktP ~ Identifier 
  def ParameterMode: Parser[Any] = inP ~ outP | inP | outP
  
  def IdentifierList: Parser[Any] = Identifier ~ kommaP ~ IdentifierList |
                                    Identifier
  def Identifier: Parser[Any] = identP
  
  def Anwfolge: Parser[Any] = Anw ~ Anwfolge | Anw
  def Anw: Parser[Any] = Zusicherung | AZuweisung | ASchleife | AFall |
                         AArrayZuweisung | AProzedurAufruf
  
  def Zusicherung: Parser[Any] = zusicherungP ~ Identifier ~ dpunktP ~ Bedingungen |
                                 zusicherungP ~ Identifier ~ dpunktP ~ Ausdruck
  def AFall: Parser[Any] = ifP ~ Bedingungen ~ thenP ~ Anwfolge ~ elseP ~ Anwfolge ~ 
                              endP ~ ifP ~ semikolonP |
                           ifP ~ Bedingungen ~ thenP ~ Anwfolge ~ endP ~ ifP ~ semikolonP        
  def AProzedurAufruf: Parser[Any] = ProzedurAufruf ~ semikolonP
  def AArrayZuweisung: Parser[Any] = ProzedurAufruf ~ zuweisungP ~ Ausdruck ~ semikolonP
  def ASchleife: Parser[Any] = whileP ~ Bedingungen ~ loopP ~ Anwfolge ~ endP ~ loopP ~ 
                                  semikolonP
  def AZuweisung: Parser[Any] = Identifier ~ zuweisungP ~ Ausdruck ~ semikolonP
  
  def Bedingungen: Parser[Any] = notP ~ openP ~ Bedingungen ~ closeP |
                                 notP ~ Bedingung |
                                 openP ~ Bedingungen ~ closeP ~ LogiOp ~ Bedingungen |
                                 openP ~ Bedingungen ~ closeP |
                                 Bedingung ~ LogiOp ~ Bedingungen |
                                 Quantor |
                                 Bedingung
                   
  def Quantor: Parser[Any] = AllQuantor | ExQuantor
  
  def AllQuantor: Parser[Any] = forallP ~ QuantorBedingungen
  def ExQuantor: Parser[Any] = existsP ~ QuantorBedingungen
  def QuantorBedingungen: Parser[Any] = Identifier ~ inP ~ openeP ~ ArrayRange ~ closeeP ~
                                          dpunktP ~ Bedingungen
  
  def Bedingung: Parser[Any] = Ausdruck ~ VergleichOp ~ Ausdruck
  def VergleichOp: Parser[Any] = geP | gtP | leP | ltP | neP | eqP
  
  def Ausdruck: Parser[Any] = Summe
  def Summe: Parser[Any] = Produkt ~ AddsOp ~ Summe | Produkt
  def Produkt: Parser[Any] = Faktor ~ MultsOp ~ Produkt | Faktor
  def Faktor: Parser[Any] = Term ~ HighPrecOp ~ Faktor | Term
  def Term: Parser[Any] = openP ~ Summe ~ closeP | ExpWert
  
  def HighPrecOp: Parser[Any] = multmultP
  def MultsOp: Parser[Any] = multP | divP | modP
  def AddsOp: Parser[Any] = plusP | minusP
  def LogiOp: Parser[Any] = andP ~ thenP | orP ~ elseP | andP | orP
  
  def AllWert: Parser[Any] = ExpWert | ArrayWerte
  
  def ExpWert: Parser[Any] = ProzedurAufruf | IntWert | Identifier | minusP ~ ExpWert
  def IntWert: Parser[Any] = numberP | ArrayKeyZugriff 
  
  def ProzedurAufruf: Parser[Any] = LinkedName ~ openP ~ WertListe ~ closeP |
                                    LinkedName ~ openP ~ closeP
  
  def WertListe: Parser[Any] = ExpWert ~ WerteListe |
                               ExpWert
  
  def WerteListe: Parser[Any] = kommaP ~ ExpWert ~ WerteListe |
                                kommaP ~ ExpWert
                                
  def ArrayDefBereich: Parser[Any] = openP ~ ArrayRange ~ closeP
  def ArrayRange: Parser[Any] = ExpWert ~ ppunktP ~ ExpWert
  def ArrayKeyZugriff: Parser[Any] = Identifier ~ firstP | Identifier ~ lastP
  def ArrayWerte: Parser[Any] = openP ~ WertListe ~ closeP
  
  def LinkedName: Parser[Any] = """([a-zA-Z]{1}(_?[a-zA-Z0-9]{1})*)
                                      (.{1}[a-zA-Z]{1}(_?[a-zA-Z0-9]{1})*){0,1}"""r
  //Lexikalische Grundelemente
  def procedureP: Parser[Any] = "procedure"
  def numberP: Parser[Any] = """[0-9]*"""r
  def identP: Parser[Any] = """[a-zA-Z]{1}(_?[a-zA-Z0-9]{1})*"""r
  def intArrayP: Parser[Any] = """(IA_){1}[a-zA-Z]{1}(_?[a-zA-Z0-9]{1})*"""r
  
  def isP: Parser[Any] = "is"
  def beginP: Parser[Any] = "begin"
  def endP: Parser[Any] = "end"
  def dpunktP: Parser[Any] = ":"
  def zuweisungP: Parser[Any] = ":="
  def punktP: Parser[Any] = "."
  def zusicherungP: Parser[Any] = "--#"
  def minusP: Parser[Any] = "-"
  def plusP: Parser[Any] = "+"
  def divP: Parser[Any] = "/"
  def multP: Parser[Any] = "*"
  def modP: Parser[Any] = "mod"
  def multmultP: Parser[Any] = "**"
  def geP: Parser[Any] = ">="
  def gtP: Parser[Any] = ">"
  def ltP: Parser[Any] = "<"
  def leP: Parser[Any] = "<="
  def neP: Parser[Any] = "/="
  def eqP: Parser[Any] = "="
  def orP: Parser[Any] = "or"
  def loopP: Parser[Any] = "loop"
  def withP: Parser[Any] = "with"
  def useP: Parser[Any] = "use"
  def andP: Parser[Any] = "and"
  def notP: Parser[Any] = "not"
  def whileP: Parser[Any] = "while"
  def elseP: Parser[Any] = "else"
  def thenP: Parser[Any] = "then"
  def ifP: Parser[Any] = "if"
  def kommaP: Parser[Any] = ","
  def closeP: Parser[Any] = ")"
  def openP: Parser[Any] = "("
  def outP: Parser[Any] = "out"
  def inP: Parser[Any] = "in"
  def semikolonP: Parser[Any] = ";"
  def integerP: Parser[Any] = "integer"
  def lastP: Parser[Any] = "'last"
  def firstP: Parser[Any] = "'first"
  def packageP: Parser[Any] = "package"
  def bodyP: Parser[Any] = "body"
  def ppunktP: Parser[Any] = ".."
  def forallP: Parser[Any] = "forall"
  def existsP: Parser[Any] = "exists"
  def openeP: Parser[Any] = "["
  def closeeP: Parser[Any] = "]"
}

object CheckAda extends AdaChecker  {  
  def par(arg: String) : Boolean = {
    var tmp:Boolean = parseAll(Programm, arg) match {
        case Success(anwf,_) => true
        case NoSuccess(msg,_) => false
    }
    return tmp
  }
  def parError(arg: String) : (String, Int, Int) = {
    var tmp = parseAll(Programm, arg) match {
        case Success(anwf,_) => ("OK", 0, 0)
        case NoSuccess(msg,str) => (msg, str.pos.line, str.pos.column)
    }
    return tmp
  }
}